from fastapi import APIRouter
from fastapi import FastAPI as FastAPI_


class FastAPI(FastAPI_):
    """
    继承原本的api
    因为原本的FastAPI没有add_resource方法，所以我们添加一个
    """

    def add_resource(self, resource, path=None, *args, **kwargs):
        """
        添加资源
        :param resource: 资源，是一个对象，拥有生成路由的方法
        :param path: 路由路径字符串，如果为None，则在资源里面传递
        :param args: 可变参数
        :param kwargs: 可变字典参数
        :return: 路由器
        """
        router = resource.generate_router(path=path)
        # 底层本质上调用的是FastAPI自带的添加子路由的方法
        self.include_router(router)


class Resource:
    # REST Ful支持的所有方法
    __methods__ = ["GET", "POST", "HEAD", "PUT", "DELETE", "CONNECT", "PATCH",
                   "TRACE", "OPTIONS", "MOVE", "COPY", "LINK", "UNLINK", "WRAPPED"]
    # REST Ful方法对应的中文名称
    # 默认中文名，用户可以通过传入 method_custom_chinese_dict 参数进行修改
    __chinese__ = ["获取", "新增", "头部", "查询", "删除", "连接", "修改",
                   "追溯", "设置", "移动", "复制", "链接", "去链接", "包裹"]

    def __init__(self, path=None, tags=[], summary="", resource_name_dict={}, method_custom_chinese_dict={}):
        '''
        path[str]               : URL资源定位符
        tags[list]              : API组名称
        summary[str]            : 资源中文名称
        resource_name_dict[dict]          : 自定义方法资源中文名称
        method_custom_chinese_dict[dict]    : 自定义方法中文名称
        '''
        self.path = path
        self.__tags__ = tags
        self.__summary__ = summary
        self.__resource_name_dict__ = resource_name_dict
        # 方法中文名称
        # 主要用于将自定义方法中的所有方法英文名转换为大写方式，比如GET，POST
        method_custom_chinese_dict = dict([(key.upper(), value) for key, value in method_custom_chinese_dict.items()])
        # 概要字典
        self.__summary_dict__ = dict(zip(self.__methods__, self.__chinese__))
        self.__summary_dict__.update(method_custom_chinese_dict)

    def generate_router(self, path=None):
        '''
        生成路由
        :param path:资源路由路径字符串
        :return: 路由器
        '''
        # 获取路径
        path = path or self.path
        # 路径不能为空
        if not path: raise Exception("path路由路径不能为空")
        router = APIRouter()
        # 查看本实例的所有方法
        self_method = filter(lambda method: not method.startswith('_') and method != 'generate_router', self.__dir__())
        for method in self_method:
            # 如果方法在REST允许的方法中
            if method.upper() in self.__methods__:
                # 资源名称，可能为空
                resource_name = self.__resource_name_dict__.get(method.lower())
                # 自动生成的名称
                auto_name = f"{self.__summary_dict__.get(method.upper())}{self.__summary__}"
                # 优先使用用户自定义的资源名称
                summary = resource_name or auto_name
                if not summary: raise Exception("summary资源概要不能为空")
                # 获取方法上的装饰器，注意，这里是路由上的装饰器，实际上是函数嵌套函数
                method_attr = getattr(router, method.lower())
                # 使用封装好的参数，重新执行一次路由的方法
                # 执行以后，得到的是嵌套的内层方法，也就是装饰器
                decorator = method_attr(path=path, tags=self.__tags__, summary=summary)
                # 通过反射设置方法
                # 这里是将方法装饰以后，重新设置到该方法上，相当于动态添加了路由
                setattr(self, method, decorator(getattr(self, method)))
        return router
